import datetime
import json

from django.http import HttpResponse
from django.shortcuts import render, redirect
from django_redis import get_redis_connection

from s25 import settings
from utils.alipay import AliPay
from utils.encrypt import uid
from web import models


def index(request):
    print("进入主页")
    return render(request, 'index.html')


def logout(request):
    request.session.flush()
    return redirect('index')


def price(request):
    """套餐"""
    # 获取套餐,肯定是只获取收费版的。
    policy_list = models.PricePolicy.objects.filter(category=2)
    return render(request, 'price.html', {'policy_list': policy_list})


def payment(request, policy_id):
    """支付页面"""
    # 1、拿到套餐id(即价格策略id)
    policy_obj = models.PricePolicy.objects.filter(id=policy_id, category=2).first()
    if not policy_obj:
        return redirect('/price')
    # 2、再拿到购买个数(查询参数中传递)
    number = request.GET.get('number', '')
    if not number or not number.isdecimal():
        return redirect('/price')
    number = int(number)
    if number < 1:
        return redirect('/price')
    # 三个if通过，说明用户正常,直接进入价格计算逻辑
    # 3、计算价格
    origin_price = number * policy_obj.price
    # 来个抵扣的钱初始化值
    balance = 0
    trans_obj = None
    # 4、要考虑到用户买了vip之后没用完，就去升级svip，要做个抵扣，但是不能你原先是svip你再买vip，按这个逻辑我还要退钱，所以你是svip就只能用完了才能买vip
    # 这个request.tracer.price_policy是我中间件查出来封装好的，是当前用户拥有的最高级别的套餐。
    if request.tracer.price_policy.category != 1:  # 如果原先用户不是付费用户(vip或svip)，就不存在抵扣的说法
        # 找到之前的订单:总支付费用，开始和结束时间，剩余天数
        trans_obj = models.Transaction.objects.filter(user=request.tracer.user, status=2).order_by('-id').first()
        # 总时间
        total_time = trans_obj.end_datatime - trans_obj.start_datatime
        # 剩余时间
        left_time = trans_obj.end_datatime - datetime.datetime.now()
        # .days将时间转换为天，就不去纠结小时了，亏几个小时不影响
        balance = trans_obj.price_policy * price * trans_obj.count / total_time.days * left_time.days
    if balance >= origin_price:  # 退的钱一定不能大于用户支付金额。
        return redirect('/price')
    context = {
        'policy_id': policy_obj.id,
        'number': number,
        'origin_price': origin_price,
        'balance': round(balance, 2),  # round(3.141592657,3)---->3.141
        'total_price': origin_price - round(balance, 2),
    }

    # 把支付信息存进redis
    conn = get_redis_connection()
    key = 'payment_{}'.format(request.tracer.user.mobile_phone)
    # 不能直接存字典，要转换为字符串
    conn.set(key, json.dumps(context), ex=60 * 30)

    context['policy_object'] = policy_obj
    context['transtation'] = trans_obj

    return render(request, 'payment.html', context)


def pay(request):
    """生成订单，去支付宝支付"""
    # 需要对用户提交的数据再次做校验，主要校验付款金额
    conn = get_redis_connection()
    key = 'payment_{}'.format(request.tracer.user.mobile_phone)
    # 支付信息(主要是核对钱),redis绝逼是存的二级制字符(基于二进制安全的)
    context_string_bytes = conn.get(key)
    if not context_string_bytes:
        return redirect("/price")
    # 支付时间没过期，拿到了参数字典
    context = json.loads(context_string_bytes.decode("utf8"))

    # 交易记录入库(待支付)，等支付成功之后需要更新status为2(同时必须给回调返回success)，并且还要计算出开始和结束时间入库
    # 来个随机订单号
    order_id = uid(request.tracer.user.mobile_phone)
    models.Transaction.objects.create(
        status=1,
        order=order_id,
        user=request.tracer.user,
        price_policy_id=context['policy_id'],
        count=context['number'],
        price=context['total_price']  # 实际支付金额
    )

    # 2、跳转到支付宝支付
    # 先生成支付链接,自己封装到utils中的
    alipay = AliPay(
        appid=settings.APPID_ALIPAY,
        app_notify_url=settings.NOTIDY_URL,
        return_url=settings.RETURN_URL,
        app_private_key_path=settings.APP_PRIVATE_PATH,
        alipay_public_key_path=settings.ALIPAY_PUBLIC_PATH
    )
    query_params = alipay.direct_pay(
        subject="tracer 平台",  # 商品简单描述
        out_trade_no=order_id,  # 商户订单号
        total_amount=context['total_price']
    )
    pay_url = "{}?{}".format(settings.ALIPAY_GATEWAY, query_params)
    return redirect(pay_url)


def pay_return(request):
    """只做跳转"""  # pay_notify接口收到post请求里我们再修改订单状态更新为2
    alipay = AliPay(
        appid=settings.APPID_ALIPAY,
        app_notify_url=settings.NOTIDY_URL,
        return_url=settings.RETURN_URL,
        app_private_key_path=settings.APP_PRIVATE_PATH,
        alipay_public_key_path=settings.ALIPAY_PUBLIC_PATH
    )
    # 只做跳转，判断是否支付成功了，不做订单的状态更新。
    # 支付吧会将订单号返回：我这边获取订单ID，然后根据订单ID做状态更新 + 认证。
    # 支付宝公钥对支付给我返回的数据request.GET 进行检查，通过则表示这是支付宝返还的接口，防请求伪造
    params = request.GET.dict()
    sign = params.pop('sign', None)
    status = alipay.verify(params, sign)
    if status:
        """
        不修改订单
        current_datetime = datetime.datetime.now()
        out_trade_no = params['out_trade_no']  # 这儿可以拿到
        _object = models.Transaction.objects.filter(order=out_trade_no).first()

        _object.status = 2
        _object.start_datetime = current_datetime
        _object.end_datetime = current_datetime + datetime.timedelta(days=365 * _object.count)
        _object.save()
        """
        pay_msg = "恭喜您，支付成功！"
        return render(request, 'payment.html', {"pay_msg": pay_msg})
    pay_msg = "对不起，您本次订单支付失败，请您重新发起支付！"
    return render(request, 'payment.html', {"pay_msg": pay_msg})


def pay_notify(request):
    """
    支付宝会不间断的向该接口一共发起八次请求，直到我这边回给它success这七个字符，支付宝才会停止请求该接口。
    不能在上一个接口就修改订单状态为已支付，它是基于js跳转，很容易被带参请求模拟----->假付款。
    """
    alipay = AliPay(
        appid=settings.APPID_ALIPAY,
        app_notify_url=settings.NOTIDY_URL,
        return_url=settings.RETURN_URL,
        app_private_key_path=settings.APP_PRIVATE_PATH,
        alipay_public_key_path=settings.ALIPAY_PUBLIC_PATH
    )
    from urllib.parse import parse_qs
    body_str = request.body.decode('utf-8')
    post_data = parse_qs(body_str)
    post_dict = {}
    for k, v in post_data.items():
        post_dict[k] = v[0]

    sign = post_dict.pop('sign', None)
    status = alipay.verify(post_dict, sign)
    if status:
        current_datetime = datetime.datetime.now()
        out_trade_no = post_dict['out_trade_no']
        _object = models.Transaction.objects.filter(order=out_trade_no).first()

        _object.status = 2
        _object.start_datetime = current_datetime
        _object.end_datetime = current_datetime + datetime.timedelta(days=365 * _object.count)
        _object.save()
        return HttpResponse('success')

    return HttpResponse('error')
